/*file chiron src/zip.js */

/*preamble-ama-chang

    Based on work placed in the public domain by Ama Chang <seijro@gmail.com>
    <http://d.hatena.ne.jp/amachang/20081130/1228029751>
    <http://svn.coderepos.org/share/lang/javascript/Zip/zip.js>

*/

/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/

/*status integrated, broken */
/*quality .1 */

include('base');
include('struct');
var utf8 = require('crypt/utf8');
var crc32 = require('crypt/crc32');
var base64 = require('crypt/base64');

var mimeType = 'application/zip';

/*** Zip
*/
exports.Zip = type(function (self, supr) {
    var members = [];
    var comment = '';

    /**** addMember
    */
    self.addMember = function (member) {
        members.push(member);
    };

    /**** addDirectory
    */
    self.addDirectory = function (name) {
        if (!ends(name, '/')) {
            name += '/';
        }
        self.addMember(DirectoryMember(name));
    };

    /**** addFile
    */
    self.addFile = function (name, data) {
        self.addMember(FileMember(name, data));
    };

    /**** data
    */
    self.data = function () {
        var output = '';
        var offsets = [];

        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            offsets.push(output.length);
            output += member.getLocalFileHeader();
            output += member.getData();
        }

        var centralDirectoryBegin = output.length;
        for (var i = 0; i < members.length; i++) {
            var member = members[i];
            output += member.getCentralDirectoryFileHeader(offsets[i]);
        }
        var centralDirectoryEnd = output.length;

        // end of central dir signature    4 bytes  (0x06054b50)
        output += pack('I', 0x06054b50);
        // number of the disk
        output += pack('H', 0);
        // number of the disk with the
        //  start of the central directory  2 bytes
        output += pack('H', 0);
        // total number of entries in the
        // central directory on this disk  2 bytes
        output += pack('H', members.length);
        // total number of entries in the central directory
        output += pack('H', members.length);
        // size of the central directory
        output += pack('I', centralDirectoryEnd - centralDirectoryBegin);
        // offset of start of central directory with respect to
        //  starting disk number
        output += pack('I', centralDirectoryBegin);
        // .zip file comment length
        output += pack('H', 0);
        // .ZIP file comment (variable size)
        output += comment;
        return output;
    };

    /**** dataUri
    */
    self.dataUri = function () {
        return [
            'data:', mimeType,
            ';base64,', base64.encode(self.data())
        ].join('');
    };

});

/*** Member
*/
exports.Member = type(function (self, supr) {
    var date, time, data, name, hash;
    var extraLocalFile = '';
    var extraCentralDirectory = ''
    var externalFileAttributes = 0;
    var internalFileAttributes = 0;
    var fileComment = '';

    /**** init
    */
    self.init = function (_name, _data) {
        name = utf8.encode(_name);
        data = _data;
        hash = crc32.hash(data);
        dt = new Date();
        date = ((dt.getFullYear() - 1980) << 9) |
               ((dt.getMonth() + 1) << 5) |
               (dt.getDate());
        time = (dt.getHours() << 5) |
               (dt.getMinutes() << 5) |
               (dt.getSeconds() >> 1);
    };

    /**** getData
    */
    self.getData = function () {
        return data;
    };

    /**** setData
    */
    self.setData = function (_data) {
        data = _data;
    };

    /**** getName
    */
    self.getName = function () {
        return name;
    };

    /**** setName
    */
    self.setName = function (_name) {
        name = _name;
    };

    /**** setExternalFileAttributes 
    */
    self.setExternalFileAttributes = function (_externalFileAttributes) {
        externalFileAttributes = _externalFileAttributes;
    };

    /**** setInternalFileAttributes 
    */
    self.setInternalFileAttributes = function (_externalFileAttributes) {
        externalFileAttributes = _externalFileAttributes;
    };

    /**** getLocalFileHeader
    */
    self.getLocalFileHeader = function () {
        var output = '';
        // local file header signature
        output += pack('I', 0x04034b50);
        // version needed to extract
        output += pack('H', 10);
        // general purpose flag
        output += pack('H', 0);
        // compression method
        output += pack('H', 0);
        // last mod file time
        output += pack('H', time);
        // last mod file date
        output += pack('H', date);
        // crc-32
        output += pack('I', hash);
        // compressed size
        output += pack('I', data.length);
        // uncompressed size
        output += pack('I', data.length);
        // file name length
        output += pack('H', name.length);
        // extra field length
        output += pack('H', extraLocalFile.length);
        // file name
        output += name;
        // extra field
        output += extraLocalFile;
        return output;
    };

    /**** getCentralDirectoryFileHeader
         - accepts the offset in the directory
    */
    self.getCentralDirectoryFileHeader = function (offset) {
        var output = '';
        // central file header signature
        output += pack('I', 0x02014b50);
        // version made by
        output += pack('H', 0x0317);
        // version needed to extract
        output += pack('H', 10);
        // general purpose bit flag
        output += pack('H', 0);
        // compression method
        output += pack('H', 0);
        // last mod file time
        output += pack('H', time);
        // last mod file date
        output += pack('H', date);
        // crc-32
        output += pack('I', hash);
        // compressed size
        output += pack('I', data.length);
        // uncompressed size
        output += pack('I', data.length);
        // file name length
        output += pack('H', name.length);
        // extra field length
        output += pack('H', extraCentralDirectory.length);
        // file comment length
        output += pack('H', fileComment.length);
        // disk number start
        output += pack('H', 0);
        // internal file attributes
        output += pack('H', internalFileAttributes);
        // external file attributes
        output += pack('I', externalFileAttributes);
        // relative offset of local header
        output += pack('I', offset);
        // file name
        output += name;
        // extra field
        output += extraCentralDirectory;
        // file comment
        output += fileComment;
        return output;
    };

});

/*** FileMember
*/
exports.FileMember = type([Member], function (self, supr) {
    self.init = function (name, data) {
        supr.init(name, data); 
        self.setExternalFileAttributes(0100644 << 16);
    };
});

/*** DirectoryMember
*/
exports.DirectoryMember = type([Member], function (self, supr) {
    self.init = function (name) {
        supr.init(name, ''); 
        self.setExternalFileAttributes((040755 << 16) | 0x10);
        /* 0x10 bit for Windows Explorer's Directory */
    };
});


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

